#include "mainwindow.h"

#include "MovieRenderer/videoframe.h"
#include "AudioRenderer/audioframe.h"
#include "Common/timeoperations.h"
#include "Common/numericoperations.h"

#include <sstream>
#include <iostream>
#include <assert.h>
#include <math.h>
#include <GL/glu.h>

using namespace std;

MainWindow::MainWindow(std::string title, int width, int height, std::string filename)
: m_pMovieRenderer(NULL)
, m_Width(0)
, m_Height(0)
, m_WindowedWidth(0)
, m_WindowedHeight(0)
, m_PrevIntervalTime(0)
, m_PrevTime(0)
, m_CurTime(0)
, m_FullScreen(false)
, m_Rotate(false)
, m_StretchToFit(false)
, m_Finished(false)
, m_Frames(0)
, m_Paused(false)
{
    if (init(title, width, height, filename))
    {
        mainloop();
    }
}

MainWindow::~MainWindow(void)
{
    destroy();
}

bool MainWindow::init(string title, int width, int height, string filename)
{
    m_Filename = filename;
    m_Title = title;
    m_Width = m_WindowedWidth = width;
    m_Height = m_WindowedHeight = height;
    m_PrevTime = m_CurTime = 0;
    m_FullScreen = false;
    m_Rotate = false;
    m_StretchToFit = false;
    m_Frames = 0;
    m_FpsText.setColor(255, 255, 255);
    m_FpsText.setSize(16);
    m_FpsText.setPosition(10.f, m_Height - 20.f);

    m_VideoOutput.initialize(m_Width, m_Height, false);
    m_VideoOutput.setWindowTitle(m_Title);

    if (!GLEE_VERSION_2_0)
    {
        cerr << "No OpenGL 2.0 support" << endl;
        return false;
    }

    if (!GLEE_ARB_multitexture)
    {
        cerr << "No multitexture support" << endl;
        return false;
    }

    m_pMovieRenderer = new MovieRenderer(m_Width, m_Height);

    if (!initGL())
    {
        destroy();
        return false;
    }

    resize(m_Width, m_Height);

    if (!filename.empty())
    {
        if (m_pMovieRenderer->setMovieFile(filename))
        {
            m_pMovieRenderer->play();
        }
    }

    return true;
}

void MainWindow::destroy()
{
}

void MainWindow::resize(int width, int height)
{
    glViewport(0, 0, width, height);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45.0, (GLfloat)m_Width/(GLfloat)m_Height, 1.0, 5.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    m_Width = width;
    m_Height = height;
    m_pMovieRenderer->setWindowSize(m_Width, m_Height);
    m_FpsText.setPosition(10.f, m_Height - 20.f);
}

void MainWindow::mainloop()
{
    while(!m_Finished && !m_VideoOutput.hasClosed())
    {
        FiledropEvent filedropEvent;
        if (m_VideoOutput.checkFiledropEvent(filedropEvent))
        {
            m_pMovieRenderer->stop();
            if (!filedropEvent.filenames.empty())
            {
                if (m_pMovieRenderer->setMovieFile(filedropEvent.filenames.front()))
                {
                    m_pMovieRenderer->play();
                }
            }
        }

        KeyEvent keyEvent;
        if (m_VideoOutput.checkKeyEvent(keyEvent))
        {
            if (keyEvent.pressed)
            {
                switch (keyEvent.keycode)
                {
                    case KeyEvent::KEY_a:
                        m_StretchToFit = ! m_StretchToFit;
                        m_pMovieRenderer->setStretchToFit(m_StretchToFit);
                        break;
                    case KeyEvent::KEY_d:
                        m_pMovieRenderer->toggleDebugInfo();
                        break;
                    case KeyEvent::KEY_f:
                        m_FullScreen = !m_FullScreen;
                        m_VideoOutput.setFullScreen(m_FullScreen);
                        if (m_FullScreen)
                        {
                            m_WindowedWidth = m_Width;
                            m_WindowedHeight = m_Height;
                        }
                        break;
                    case KeyEvent::KEY_h:
                        m_pMovieRenderer->toggleOsd();
                        break;
                    case KeyEvent::KEY_q:
                        m_Finished = true;
                        break;
                    case KeyEvent::KEY_r:
                        m_Rotate = !m_Rotate;
                        break;
                    case KeyEvent::KEY_v:
                        m_VideoOutput.toggleVSync();
                        break;
                    case KeyEvent::KEY_x:
                        m_pMovieRenderer->flipHorizontal();
                        break;
                    case KeyEvent::KEY_y:
                        m_pMovieRenderer->flipVertical();
                        break;
                    case KeyEvent::KEY_SPACE:
                        m_Paused = !m_Paused;
                        m_Paused ? m_pMovieRenderer->pause() : m_pMovieRenderer->play();
                        break;
                    case KeyEvent::KEY_LEFT:
                        m_pMovieRenderer->seek(-10);
                        break;
                    case KeyEvent::KEY_RIGHT:
                        m_pMovieRenderer->seek(10);
                        break;
                    case KeyEvent::KEY_UP:
                        m_pMovieRenderer->adjustVolume(0.1f);
                        break;
                    case KeyEvent::KEY_DOWN:
                        m_pMovieRenderer->adjustVolume(-0.1f);
                        break;
                    case KeyEvent::KEY_F1:
                        m_pMovieRenderer->toggleShader(DATADIR"/glover/shaders/sharpen.frag");
                        break;
                    case KeyEvent::KEY_F2:
                        m_pMovieRenderer->toggleShader(DATADIR"/glover/shaders/blur.frag");
                        break;
                    case KeyEvent::KEY_F3:
                        m_pMovieRenderer->toggleShader(DATADIR"/glover/shaders/edge.frag");
                        break;
                    case KeyEvent::KEY_F4:
                        m_pMovieRenderer->toggleShader(DATADIR"/glover/shaders/negative.frag");
                        break;
                    case KeyEvent::KEY_F5:
                        m_pMovieRenderer->adjustBrightness(-0.05f);
                        break;
                    case KeyEvent::KEY_F6:
                        m_pMovieRenderer->adjustBrightness(0.05f);
                        break;
                    case KeyEvent::KEY_F7:
                        m_pMovieRenderer->adjustGamma(0.05f);
                        break;
                    case KeyEvent::KEY_F8:
                        m_pMovieRenderer->adjustGamma(-0.05f);
                        break;
                    case KeyEvent::KEY_F9:
                        m_pMovieRenderer->adjustContrast(-0.05f);
                        break;
                    case KeyEvent::KEY_F10:
                        m_pMovieRenderer->adjustContrast(0.05f);
                        break;
                    case KeyEvent::KEY_F12:
                        m_pMovieRenderer->resetImageSettings();
                        break;
                    default:
                        break;
                }
            }
        }

        ResizeEvent resizeEvent;
        if (m_VideoOutput.checkResizeEvent(resizeEvent))
        {
            resize(resizeEvent.width, resizeEvent.height);
        }

        m_CurTime = TimeOperations::getTimeInMilliSeconds();
        drawGL();
        m_Frames++;

        if ((m_CurTime - m_PrevIntervalTime) > 1000)
        {
            string fps;
            NumericOperations::toString(m_Frames, fps);
            m_FpsText.setText(fps);

            m_Frames = 0;
            m_PrevIntervalTime = m_CurTime;
        }
        m_PrevTime = m_CurTime;
    }
}

void MainWindow::drawGL()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    GLfloat aspect = (GLfloat) m_Width / m_Height;
    GLfloat hFov = 40.0; //tan(40/2) = 0.36397023426620236135104788277683 (degrees)
    static int rotation = 0;

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(hFov, aspect, 3.0, 5000);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    GLfloat distance = (float)m_Width/2.f/0.36397023426620236135104788277683f / aspect;
    gluLookAt(m_Width/2, m_Height/2, distance, m_Width/2, m_Height/2, 0, 0, 1, 0);

    glPushMatrix();

    if (m_Rotate)
    {
        ++rotation;
        rotation %= 360;
    }

    glTranslatef(m_Width/2.f, m_Height/2.f, 0);
    glRotatef((GLfloat)rotation, 0.f, 1.f, 0.f);
    glTranslatef(-m_Width/2.f, -m_Height/2.f, 0);

    m_pMovieRenderer->render(m_CurTime - m_PrevTime);
    glPopMatrix();

    drawText(m_FpsText);

    m_VideoOutput.swapBuffers();

    assert(glGetError() == 0);
}

void MainWindow::drawText(GL::Text& text)
{
    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();
    gluOrtho2D(0, m_Width-1, 0, m_Height-1);

    text.render();

    glMatrixMode(GL_PROJECTION);
    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);
}

bool MainWindow::initGL()
{
    glShadeModel(GL_SMOOTH);                            // Enable Smooth Shading
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);               // Black Background
    glClear(GL_COLOR_BUFFER_BIT);
    glClearDepth(1.0f);                                 // Depth Buffer Setup
    glDisable(GL_DEPTH_TEST);                           // Enables Depth Testing
    glDepthFunc(GL_LESS);                               // The Type Of Depth Testing To Do
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);  // Really Nice Perspective Calculations

    glDisable(GL_MULTISAMPLE_ARB);

    return !glGetError();
}


